<template>
    <div class="my-cropper" ref="myCropper" @mousewheel.stop.prevent="mouseWheel">
        <!--原图区域-->
        <div class="original-box">
            <div class="original-image-box"
                 :style="{
                    'width': imgBox.width + 'px',
                    'height': imgBox.height + 'px',
                    'transform': imgBoxStyle
                }">
                <img :src="originImage"/>
            </div>
        </div>
        <!--阴影-->
        <div class="outer-shadow" @mousedown.stop.prevent="mouseDownShadow"></div>

        <!--切图区域-->
        <div class="cut-box"
             @mousedown.stop.prevent="mouseDownCut"
             :style="{
                    'width': cutBox.width + 'px',
                    'height': cutBox.height + 'px',
                    'transform': `translate(${cutBox.moveLeft}px, ${cutBox.moveTop}px)`
             }">
            <div class="resolution" v-if="showResolution">
                {{cutBox.width}} × {{cutBox.height}}
            </div>
            <div class="cut-image-box">
                <img :src="originImage"
                     :style="{
                        'width': imgBox.width + 'px',
                        'height': imgBox.height + 'px',
                        'transform': cutImgStyle
                    }"
                />
            </div>
            <div v-if="enableCut">
                <!--操作线-->
                <div class="action-line left-line" @mousedown.stop.prevent="(e) => changeSideDown(e,'left')"></div>
                <div class="action-line top-line" @mousedown.stop.prevent="(e) => changeSideDown(e,'top')"></div>
                <div class="action-line right-line" @mousedown.stop.prevent="(e) => changeSideDown(e,'right')"></div>
                <div class="action-line bottom-line" @mousedown.stop.prevent="(e) => changeSideDown(e,'bottom')"></div>
                <!--操作球-->
                <!--顶部-->
                <div class="action-point top-left-point" @mousedown.stop.prevent="(e) => changeSideDown(e,'top-left')"></div>
                <div class="action-point top-center-point" @mousedown.stop.prevent="(e) => changeSideDown(e,'top')"></div>
                <div class="action-point top-right-point" @mousedown.stop.prevent="(e) => changeSideDown(e,'top-right')"></div>
                <!--右侧-->
                <div class="action-point right-center-point" @mousedown.stop.prevent="(e) => changeSideDown(e,'right')"></div>
                <div class="action-point right-bottom-point" @mousedown.stop.prevent="(e) => changeSideDown(e,'bottom-right')"></div>
                <!--下侧-->
                <div class="action-point bottom-center-point" @mousedown.stop.prevent="(e) => changeSideDown(e,'bottom')"></div>
                <div class="action-point bottom-left-point" @mousedown.stop.prevent="(e) => changeSideDown(e,'bottom-left')"></div>
                <!--左侧-->
                <div class="action-point left-center-point" @mousedown.stop.prevent="(e) => changeSideDown(e,'left')"></div>
            </div>
        </div>
    </div>
</template>

<script>
    import infoInitMixin from "./info-init-mixin"
    export default {
        mixins: [infoInitMixin],
        props: {
            // 原图
            originImage: {
                type: String,
                default: ""
            },
            // 展示分辨率
            showResolution: {
                type: Boolean,
                default: true
            },

            // 能否改变切图区域尺寸
            enableCut: {
                type: Boolean,
                default: false
            },
            // 默认切图区域宽度
            defaultCutWidth: {
                type: Number,
                default: 0
            },
            // 默认切图区域高度
            defaultCutHeight: {
                type: Number,
                default: 0
            },
        },
        computed: {
            //原图的内联样式
            imgBoxStyle() {
                let imgBox = this.imgBox
                return `translate(${imgBox.moveLeft}px, ${imgBox.moveTop}px) rotateZ(${imgBox.rotate})`
            },
            //切块图的内联样式
            cutImgStyle() {
                let imgBox = this.imgBox
                let cutBox = this.cutBox
                let leftPosition = imgBox.moveLeft - cutBox.moveLeft
                let topPosition = imgBox.moveTop - cutBox.moveTop
                return `translate(${leftPosition}px, ${topPosition}px) rotateZ(${imgBox.rotate})`
            }
        },
        data() {
            return {
                //鼠标按下事件全局
                mouseDown: {
                    event: null,

                    //鼠标按下的时候，那时候 cutBox的位置
                    cutBoxMoveLeft: 0,
                    cutBoxMoveTop: 0,

                    //changeSide专用
                    position: '',
                    // 鼠标按下的时候，那时候 cutBox的宽高
                    downCutBoxWidth: 0,
                    downCutBoxHeight: 0,


                    // 鼠标按下的时候，那时候 imgBox的位置
                    imgBoxMoveLeft: 0,
                    imgBoxMoveTop: 0
                },

            }
        },
        methods: {
            // 鼠标按下阴影
            mouseDownShadow(e) {
                this.mouseDown.event = e

                this.mouseDown.imgBoxMoveLeft = this.imgBox.moveLeft

                this.mouseDown.imgBoxMoveTop = this.imgBox.moveTop

                window.addEventListener("mousemove", this.mouseMoveShadow);
                window.addEventListener("mouseup", this.mouseUp);
            },
            mouseMoveShadow(e) {
                const moveX = e.clientX
                const moveY = e.clientY

                const disX = this.mouseDown.event.clientX - this.mouseDown.imgBoxMoveLeft

                const disY = this.mouseDown.event.clientY - this.mouseDown.imgBoxMoveTop

                //实际的偏移量
                let nX = moveX - disX
                let nY = moveY - disY

                this.imgBox.moveLeft = nX

                this.imgBox.moveTop = nY

                //生成图片
                this.generateImg()
            },

            // 鼠标按下边框
            changeSideDown(e, position) {
                this.mouseDown.event = e

                this.mouseDown.cutBoxMoveLeft = this.cutBox.moveLeft

                this.mouseDown.cutBoxMoveTop = this.cutBox.moveTop

                this.mouseDown.downCutBoxWidth = this.cutBox.width

                this.mouseDown.downCutBoxHeight = this.cutBox.height

                this.mouseDown.position = position

                window.addEventListener("mousemove", this.changeSideMove);

                window.addEventListener("mouseup", this.mouseUp);
            },
            changeSideMove(e) {

                const moveX = e.clientX
                const moveY = e.clientY

                const disX = this.mouseDown.event.clientX - this.mouseDown.cutBoxMoveLeft

                const disY = this.mouseDown.event.clientY - this.mouseDown.cutBoxMoveTop

                //实际的偏移量
                let nX = moveX - disX
                let nY = moveY - disY

                //按下值 与 移动值 的差值
                let diffX = moveX - this.mouseDown.event.clientX
                let diffY = moveY - this.mouseDown.event.clientY

                //移动方向
                const position = this.mouseDown.position

                // 鼠标按下的时候，cutBox改变前的宽高值
                const downWidth = this.mouseDown.downCutBoxWidth

                const downHeight = this.mouseDown.downCutBoxHeight

                // 鼠标按下的时候，cutBox改变前的上下偏移量
                const downMoveLeft = this.mouseDown.cutBoxMoveLeft

                const downMoveTop = this.mouseDown.cutBoxMoveTop

                //左边移动
                const doMoveLeft = ()=> {
                    this.cutBox.width = Math.abs(downWidth - diffX)

                    // 反转控制
                    if(downWidth - diffX <= 0) {
                        //如果宽度已经为0 那么偏移量将不再增加
                        this.cutBox.moveLeft = downMoveLeft + downWidth
                    }else {
                        this.cutBox.moveLeft = nX
                    }

                    // 边界控制
                    if(downWidth - diffX <= 0 && nX >= this.outerBox.width) {
                        // 反转的情况下，碰到了边界
                        // 这种情况下 this.cutBox.moveLeft 永远是同一个值 所以宽度只需要 外框 - 偏移量 就能得到临界值
                        this.cutBox.width = this.outerBox.width - this.cutBox.moveLeft
                    }
                    else if(nX <= 0) {
                        //普通情况下碰到了边界
                        this.cutBox.width = Math.abs(downWidth + downMoveLeft)
                        this.cutBox.moveLeft = 0
                    }
                }

                //右边移动
                const doMoveRight = ()=> {
                    this.cutBox.width = Math.abs(downWidth + diffX)

                    if( downWidth + diffX <= 0) {
                        this.cutBox.moveLeft = downMoveLeft - this.cutBox.width
                    }else {
                        this.cutBox.moveLeft = downMoveLeft
                    }

                    // 边界控制
                    if( downWidth + diffX <= 0 && this.cutBox.moveLeft <= 0) {
                        this.cutBox.moveLeft = 0
                        this.cutBox.width = downMoveLeft - this.cutBox.moveLeft
                    }else if(this.cutBox.moveLeft + this.cutBox.width >= this.outerBox.width) {
                        this.cutBox.width = this.outerBox.width - downMoveLeft
                    }
                }

                //上移动
                const doMoveTop = ()=> {
                    this.cutBox.height = Math.abs(downHeight - diffY)

                    // 反转控制
                    if(downHeight - diffY <= 0) {
                        //如果宽度已经为0 那么偏移量将不再增加
                        this.cutBox.moveTop = downMoveTop + downHeight
                    }else {
                        this.cutBox.moveTop = nY
                    }

                    // 边界控制
                    if(downHeight - diffY <= 0 && nY >= this.outerBox.height) {
                        this.cutBox.height = this.outerBox.height - this.cutBox.moveTop
                    }else if(nY <= 0){
                        this.cutBox.moveTop = 0
                        this.cutBox.height = downHeight + downMoveTop
                    }
                }

                //下移动
                const doMoveBottom = ()=> {
                    this.cutBox.height = Math.abs( downHeight + diffY)

                    // 反转控制
                    if( downHeight + diffY <= 0) {
                        this.cutBox.moveTop = downMoveTop - this.cutBox.height
                    }else {
                        this.cutBox.moveTop = downMoveTop
                    }

                    // 边界控制
                    if( downHeight + diffY <= 0 && this.cutBox.moveTop <= 0) {
                        this.cutBox.moveTop = 0
                        this.cutBox.height = downMoveTop - this.cutBox.moveTop
                    }else if(this.cutBox.moveTop + this.cutBox.height >= this.outerBox.height) {
                        this.cutBox.height = this.outerBox.height - downMoveTop
                    }
                }

                switch (position) {
                    case 'left':
                        doMoveLeft()
                        break
                    case 'bottom':
                        doMoveBottom()
                        break;
                    case 'right':
                        doMoveRight()
                        break
                    case 'top':
                        doMoveTop()
                        break
                    case 'top-left':
                        doMoveTop()
                        doMoveLeft()
                        break
                    case 'top-right':

                        doMoveTop()
                        doMoveRight()

                        break
                    case 'bottom-left':
                        doMoveBottom()
                        doMoveLeft()
                        break
                    case 'bottom-right':
                        doMoveBottom()
                        doMoveRight()
                        break
                }


                //生成图片
                this.generateImg()
            },
            //切块区域的鼠标按下
            mouseDownCut(e) {

                this.mouseDown.event = e

                this.mouseDown.cutBoxMoveLeft = this.cutBox.moveLeft

                this.mouseDown.cutBoxMoveTop = this.cutBox.moveTop


                window.addEventListener("mousemove", this.mouseCutMove);

                window.addEventListener("mouseup", this.mouseUp);
            },
            mouseCutMove(e) {

                const disX = this.mouseDown.event.clientX - this.mouseDown.cutBoxMoveLeft

                const disY = this.mouseDown.event.clientY - this.mouseDown.cutBoxMoveTop

                let nX = e.clientX - disX
                let nY = e.clientY - disY

                // 左大框边界
                if(nX <= 0) {
                    nX = 0
                }
                if(nY <= 0) {
                    nY = 0
                }

                // 右大框边界限制
                if(nX >= this.outerBox.width - this.cutBox.width) {
                    nX = this.outerBox.width - this.cutBox.width
                }
                if(nY >= this.outerBox.height - this.cutBox.height) {
                    nY = this.outerBox.height - this.cutBox.height
                }

                this.cutBox.moveLeft = nX

                this.cutBox.moveTop = nY

                //生成图片
                this.generateImg()
            },
            mouseUp() {
                // 移动切图
                window.removeEventListener("mousemove", this.mouseCutMove);
                // 改变切图
                window.removeEventListener("mousemove", this.changeSideMove);
                // 拖拽原图
                window.removeEventListener("mousemove", this.mouseMoveShadow);
                window.removeEventListener("mouseup", this.mouseUp);
            },
            // 生成canvas
            generateImg() {
                const canvas = document.createElement("canvas");
                const ctx = canvas.getContext("2d");
                canvas.width = this.cutBox.width
                canvas.height = this.cutBox.height

                let img = new Image();

                img.src = this.originImage;

                const showWidth = this.imgBox.width

                const showHeight = this.imgBox.height

                let imgStartX = this.imgBox.moveLeft - this.cutBox.moveLeft

                let imgStartY = this.imgBox.moveTop - this.cutBox.moveTop

                img.onload = () => {

                    ctx.drawImage(img, imgStartX, imgStartY, showWidth, showHeight);

                    const base64Data = canvas.toDataURL("image/png",1)

                    this.$emit('getImg',base64Data)
                }
            },
            // 鼠标滚轮缩放
            mouseWheel(e) {
                let wheelValue = e.deltaY

                //大于0 向上走
                if(wheelValue > 0) {
                    //不能再缩小了
                    if(this.imgBox.scale > 0.1) {
                        this.imgBox.scale -= 0.1
                    }
                }else {

                    this.imgBox.scale += 0.1
                }
                this.imgBox.scale = Number(this.imgBox.scale.toFixed(1))

                let oldWidth = this.imgBox.initWidth

                let oldHeight = this.imgBox.initHeight

                //上一个宽度
                let lastWidth = this.imgBox.width

                //上一个高度
                let lastHeight = this.imgBox.height

                this.imgBox.width = oldWidth * this.imgBox.scale

                this.imgBox.height = oldHeight * this.imgBox.scale

                //偏移量设置 以图片为中心缩小
                this.imgBox.moveLeft = this.imgBox.moveLeft + (lastWidth - this.imgBox.width) / 2

                this.imgBox.moveTop = this.imgBox.moveTop + (lastHeight - this.imgBox.height) / 2

                this.generateImg()

            }
        },
        mounted() {
            this.getOuterBoxInfo()
            if(this.originImage) {
                this.changeImage(this.originImage)
            }
        },
        destroyed() {
            window.removeEventListener("mousemove", this.mouseCutMove);
            window.removeEventListener("mousemove", this.changeSideMove);
            window.removeEventListener("mouseup", this.mouseUp);
        },
        watch: {
            originImage(v) {
                this.changeImage(v)
            }
        }
    }
</script>

<style scoped lang="scss">
    @mixin fullShow {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        user-select: none;
    }

    .my-cropper {
        position: relative;
        width: 100%;
        height: 100%;
        box-sizing: border-box;
        user-select: none;
        background-image: url();
        .outer-shadow {
            cursor: move;
            @include fullShow;
            background-color: rgba(0,0,0,.5);
        }

        .original-box {
            overflow: hidden;
            @include fullShow;
            .original-image-box {
                @include fullShow;
                img {
                    user-select: none;
                    -webkit-user-drag: none;
                    //max-width: 100%;
                    height: 100%;
                }
            }
        }

        .cut-box {
            @include fullShow;
            cursor: move;
            background-color: rgba(255,255,255,0.2);
            outline: 1px solid rgba(51,153,255,.75);
            .resolution {
                padding: 0 4px;
                position: absolute;
                font-size: 12px;
                top: 0;
                left: 0;
                transform: translateY(-100%);
                color: #ffffff;
                background-color: rgba(0,0,0,0.4);
            }
            .cut-image-box {
                height: 100%;
                width: 100%;
                overflow: hidden;
                img {
                    user-select: none;
                    -webkit-user-drag: none;
                }
            }

            // 操作线的粗细
            $lineBold: 5px;
            // 线冒出框框外一点
            $linePosition: -3px;
            .action-line {
                position: absolute;
                &.left-line {
                    cursor: w-resize;
                    top: 0;
                    left: $linePosition;
                    width: $lineBold;
                    height: 100%;
                }
                &.top-line {
                    cursor: n-resize;
                    top: $linePosition;
                    left: 0;
                    height: $lineBold;
                    width: 100%;
                }
                &.right-line {
                    cursor: e-resize;
                    top: 0;
                    right: $linePosition;
                    height: 100%;
                    width: $lineBold;
                }
                &.bottom-line {
                    cursor: s-resize;
                    bottom: $linePosition;
                    left: 0;
                    height: $lineBold;
                    width: 100%;
                }
            }

            // 球向外移动的位置
            $pointPosition: -4px;
            // 操作球
            .action-point {
                position: absolute;
                background-color: rgba(51,153,255,.75);
                width: 8px;
                height: 8px;
                border-radius: 100%;
                &.top-left-point {
                    top: $pointPosition;
                    left: $pointPosition;
                    cursor: nw-resize;
                }
                &.top-center-point {
                    top: $pointPosition;
                    right: 0;
                    left: 0;
                    margin: 0 auto;
                    cursor: n-resize;
                }
                &.top-right-point {
                    top: $pointPosition;
                    right: $pointPosition;
                    cursor: ne-resize;
                }
                &.right-center-point {
                    top: 0;
                    bottom: 0;
                    right: $pointPosition;
                    margin: auto 0;
                    cursor: e-resize;
                }
                &.right-bottom-point {
                    bottom: $pointPosition;
                    right: $pointPosition;
                    cursor: se-resize;
                }
                &.bottom-center-point {
                    bottom: $pointPosition;
                    right: 0;
                    left: 0;
                    margin: 0 auto;
                    cursor: s-resize;
                }
                &.bottom-left-point {
                    bottom: $pointPosition;
                    left: $pointPosition;
                    cursor: sw-resize;

                }
                &.left-center-point {
                    left: $pointPosition;
                    top: 0;
                    bottom: 0;
                    margin: auto 0;
                    cursor: w-resize;
                }
            }
        }
    }
</style>